#ifndef __TCPSERVER_HPP__
#define __TCPSERVER_HPP__

#include "MyLog.hpp"
#include "MySock.hpp"
#include "Epoll.hpp"
#include "MultiPlayerChatRoom.hpp"
#include <functional>
#include <unordered_map>

// TCPServer服务器封装
namespace QiHai
{
    class Connect;  // 声明
    class TCPServer;  // 声明
    using func_t = std::function<void(Connect*)>;
    using call_t = std::function<void(std::unordered_map<int, Connect*>&, Connect*, const std::string&)>;
    // 连接对象，即每个sock包括监听套接字的一个管理对象，里面存在两种缓冲区和三种执行方法，对应事件发生分别执行对应方法
    // 每一个连接对象都默认监听读取事件进入epoll模型，只有读取成功后执行对应方法会执行写入
    class Connect
    {
    public:
        int _sock;
        TCPServer* _server;

        std::string _readBuffer;  // 用户接收缓冲区
        std::string _writeBuffer;  // 用户发送缓冲区

        func_t _readFunc;  // 读方法
        func_t _writeFunc;  // 写方法
        func_t _abnormalFunc;  // 异常处理方法

        std::string _connectIp;  // 服务套接字对应客户端的ip
        uint16_t _connectPort;  // 服务套接字对应客户端的port
    public:
        Connect(int sock, TCPServer* server, func_t rf, func_t wf, func_t af);
        // 设置此连接对象ip和port用于区分
        void setIpandPort(const std::pair<std::string, uint16_t>& data);
    };

    // TCPServer对象封装
    class TCPServer
    {
        // 声明一下友元类
        friend class Connect;
    private:
        int _listenSock;  // 监听套接字
        Connect* _listenConnect;  // 监听连接对象
        EPoll _epoll;  // epoll模型
        std::unordered_map<int, Connect*> _users;// 组织连接对象
        struct epoll_event* _events;  // 监听返回数组
        int _maxNum;  // 大致个数

        call_t _call;  // 上层调用方法
    public:
        TCPServer(uint16_t port, call_t call = nullptr, const std::string& ip = "0.0.0.0");
        TCPServer(const TCPServer&) = delete;
        ~TCPServer();

        // 修改上层调用方法
        void SetCall(call_t call);
        // 监听套接字的读处理方法执行
        void Accepter(Connect* connect);
        // 读处理方法
        void Reader(Connect* connect);
        // 写处理方法
        void Writer(Connect* connect);
        // 异常处理方法
        void Abnormal(Connect* connect);
        // 修改sock在epoll的监听方法
        void SetSockRW(int sock, bool read, bool write);

        // 一轮等待执行策略
        void RoundExe();
        // 启动服务器
        void Strat();
    };

    // Connect
    // 套接字对象 服务器对象 读方法 写方法 异常方法
    Connect::Connect(int sock, TCPServer* server, func_t rf, func_t wf, func_t af)
    :_sock(sock), _server(server), _readFunc(rf), _writeFunc(wf), _abnormalFunc(af)
    {
        // 先对文件描述符设置非阻塞读取-才能读取完不会被阻塞等待
        if (!MySock::IsNoBlock(sock))
        {
            LOG(FATAL) << "IsNoBlock error" << std::endl;
            exit(4);
        }
        // 初始化第一个连接均加入读事件监听
        if (!_server->_epoll.AddEPollIn(sock))
        {
            LOG(FATAL) << "ADDEPollIn error" << std::endl;
            exit(5);
        }
    }

    void Connect::setIpandPort(const std::pair<std::string, uint16_t>& data)
    {
        _connectIp = data.first;
        _connectPort = data.second;
    }

    // TcpServer
    TCPServer::TCPServer(uint16_t port, call_t call, const std::string& ip)
    :_listenSock(-1), _listenConnect(nullptr), _call(call), _events(nullptr), _maxNum(126)
    {
        _events = new epoll_event[_maxNum];
        // 获取套接字文件描述符
        _listenSock = MySock::Socket();
        if (_listenSock == -1)
        {
            LOG(FATAL) << "listsock init error!" << std::endl;
            exit(1);
        }
        // 给对应套接字绑定ip和port
        if (!MySock::Bind(_listenSock, ip, port))
        {
            LOG(FATAL) << "Server bind ip and port error!" << std::endl;
            exit(2);
        }
        // 设置监听套接字
        if (!MySock::ListenSock(_listenSock))
        {
            LOG(FATAL) << "listensock init error!" << std::endl;
            exit(3);
        }

        // 设置epoll模型为ET边缘触发模式
        _epoll.SetWorkMode(WorkMode::ET);
        // 增加连接对象监听套接字
        _listenConnect = new Connect(_listenSock, this, std::bind(&TCPServer::Accepter, this, std::placeholders::_1), nullptr, nullptr);  // 因为类方法需要传this参数
        // 注意，只有监听套接字不加入user中，它不属于连接
        LOG(NORMAL) << "TCPServer init success! _listenSock read..." << std::endl;
    }
    TCPServer::~TCPServer()
    {
        close(_listenSock);
        if (_listenConnect) delete _listenConnect;
        _listenConnect = nullptr;
        delete[] _events;
    }

    void TCPServer::Accepter(Connect* connect)
    {
        // 执行时候已经就绪了
        // 注意设置的是非阻塞模式
        std::pair<std::string, uint16_t> tmp;
        int errorNum;
        while (true)
        {
            int sock = MySock::Accept(connect->_sock, &tmp, &errorNum);
            // sock > 0 读取成功  < 0 根据非阻塞条件判断是否出现错误
            if (sock > 0)
            {
                // 为此连接增加connect对象
                Connect* client = new Connect(sock, this, std::bind(&TCPServer::Reader, this, std::placeholders::_1),\
                std::bind(&TCPServer::Writer, this, std::placeholders::_1),\
                std::bind(&TCPServer::Abnormal, this, std::placeholders::_1));
                // 设置此连接的Ip和Port
                client->setIpandPort(tmp);
                // 增加user
                _users[sock] = client;
                LOG(NORMAL) << "new accept [" << tmp.first << ": " << tmp.second << "]......" << std::endl;

                // 发送到公共聊天区域，表示用户加入
                Request req(("欢迎新用户[" + tmp.first + ": " + std::to_string(tmp.second) + "]加入！"), 1);
                if (_call == nullptr)
                {
                    LOG(FATAL) << "_call no setting!" << std::endl;
                    exit(8);
                }
                _call(_users, connect, req.Serialization());
            }
            else
            {
                // 此时根据返回的errorNum进行判断是否出现错误
                if (errorNum == EWOULDBLOCK || errorNum == EAGAIN) break;  // 没有连接了 正常情况
                else if (errorNum == EINTR)
                {
                    LOG(WARING) << "Accept IO NTR..." << std::endl;
                    continue;  // IO中断，重试一次即可
                }
                else{
                    // 此时是真正的错误，差错处理
                    LOG(ERROR) << "Accept error:" << strerror(errorNum) << std::endl;
                    break;  // 错误没有太大的影响，退出即可
                }
            }
        }
    }

    void TCPServer::Reader(Connect* connect)
    {
        // 此时，需要将内核的读取缓冲区循环读完（非阻塞），读取完毕后利用我们的协议进行切割分组，如果没有发完留到读取缓冲区中，等下次拼接完整
        // 调用此方法的时候，内核读缓冲区一定存在数据了
        char buffer[10240];
        while (true)
        {
            ssize_t n = read(connect->_sock, buffer, 10240);
            if (n > 0)
            {
                // 大于零 读取成功
                buffer[n] = '\0';
                connect->_readBuffer += buffer;  // 添加到读取缓冲区
            }
            else if (n == 0)
            {
                // 对方关闭连接
                LOG(NORMAL) << "[" << connect->_connectIp << ": " << connect->_connectPort << "] not connect, close sock..." << std::endl;
                connect->_abnormalFunc(connect);  // 执行异常调用方法 - 清除连接对象，清除epoll模型中sock
                return;
            }
            else{
                // n < 0 判断是否错误
                if (errno == EWOULDBLOCK || errno == EAGAIN) break;  // 读取完毕
                else if (errno == EINTR)
                {
                    LOG(WARING) << "[" << connect->_connectIp << ": " << connect->_connectPort << "] read IO NTR..." << std::endl;
                    continue;
                }
                else
                {
                    // error
                    LOG(ERROR) << "[" << connect->_connectIp << ": " << connect->_connectPort << "] read error..." << std::endl;
                    connect->_abnormalFunc(connect);
                    return;
                }
            }
        }
        // 能够运行到这里说明上面均没有问题，我们这里进行结构分析拆分 解决粘包问题
        std::vector<std::string> messages;
        SpliteMessage(connect->_readBuffer, messages);
        // 提取出来后执行上层调用方法
        if (_call == nullptr)
        {
            LOG(FATAL) << "_call no setting!" << std::endl;
            exit(6);
        }
        for (auto& str : messages)
        {
            _call(_users, connect, str);  // 上层调用设置监听
            // 公共聊天室，需要给每一个用户发送消息
        }
    }

    void TCPServer::Writer(Connect* connect)
    {
        // 内核发送缓冲区空位
        // 将connect的用户发送缓冲区发送完或者内核发送缓冲区满了中止行为
        while (true)
        {
            ssize_t n = write(connect->_sock, connect->_writeBuffer.c_str(), connect->_writeBuffer.size());  // 全部发送
            if (n > 0)
            {
                // 发送成功，注意查收发送了多少，并且删除发送的部分
                connect->_writeBuffer.erase(0, n);
                if (connect->_writeBuffer.empty()) break;  // 发送完了，退出循环
            }
            else
            {
                if (errno == EWOULDBLOCK || errno == EAGAIN) break;  // 内核发送缓冲区满了，退出等待就绪
                else if (errno == EINTR)
                {
                    LOG(WARING) << "[" << connect->_connectIp << ": " << connect->_connectPort << "] write IO NTR..." << std::endl;
                    continue;
                }
                else
                {
                    // error
                    LOG(ERROR) << "[" << connect->_connectIp << ": " << connect->_connectPort << "] write error..." << std::endl;
                    connect->_abnormalFunc(connect);  // 执行异常处理
                    return;
                }
            }
        }
        // 判断缓冲区是否为空，如果为空那么关闭其写事件的监听
        if (connect->_writeBuffer.empty()) _epoll.ModEPollIn(connect->_sock);
    }

    void TCPServer::Abnormal(Connect* connect)
    {
        // 异常处理，此时对方关闭连接或者出现错误，都需要释放其连接
        // 先移除epoll中的监听
        if (!_epoll.DelEPoll(connect->_sock))
        {
            LOG(FATAL) << "_epoll del error!" << std::endl;
            exit(7);
        }
        // 在users管理中删除此连接对象
        _users.erase(connect->_sock);
        // 关闭连接
        close(connect->_sock);
        // 释放connect内存
        delete connect;
    }

    void TCPServer::SetCall(call_t call)
    {
        _call = call;
    }

    void TCPServer::RoundExe()
    {
        // epoll模型等待

        int n = _epoll.WaitEPoll(_events, _maxNum);
        if (n < 0)
        {
            LOG(FATAL) << "WaitEPoll error!" << std::endl;
            exit(8);
        }
        for (int i = 0; i < n; ++i)
        {
            int sock = _events[i].data.fd;
            int event = _events[i].events;

            // 监听套接字特殊处理
            if (sock == _listenSock)
            {
                _listenConnect->_readFunc(_listenConnect);
                continue;
            }

            if ((event & EPOLLERR) || (event & EPOLLHUP)) event |= (EPOLLIN | EPOLLOUT);  // 交给后面的读和写进行异常处理即可

            if (event & EPOLLIN)
            {
                if (_users[sock]->_readFunc)  // 判断其读取方法是否设置
                    _users[sock]->_readFunc(_users[sock]);
            }
            if (event & EPOLLOUT)
            {
                if (_users[sock]->_writeFunc)  // 判断其读取方法是否设置
                    _users[sock]->_writeFunc(_users[sock]);
            }
        }
    }

    void TCPServer::Strat()
    {
        // 服务器启动监听。死循环开始
        while (true)
        {
            RoundExe();
        }
    }

    void TCPServer::SetSockRW(int sock, bool read, bool write)
    {
        if (read & write)
            _epoll.ModeEPollInOut(sock);
        else if (read)
            _epoll.ModEPollIn(read);
        else _epoll.ModEPollOut(read);
    }


}
#endif